package com.eastfair.venueservice.service;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.eastfair.boot.request.PageParams;
import com.eastfair.boot.service.SuperService;
import com.eastfair.common.dto.CommonServiceBookingDTO;
import com.eastfair.constant.BusinessConstant;
import com.eastfair.pay.dto.TransactionOrderDTO;
import com.eastfair.pay.enumeration.TransactionOrderOrderStatusEnum;
import com.eastfair.pay.enumeration.TransactionOrderOrderTypeEnum;
import com.eastfair.venueservice.dto.ServiceBookingDTO;
import com.eastfair.venueservice.dto.ServiceOrderDTO;
import com.eastfair.venueservice.entity.ServiceOrder;
import com.eastfair.venueservice.enumeration.ExhibitionSettlementDetailSettlementTypeEnum;
import com.eastfair.venueservice.enumeration.ServiceOrderInvoiceStatusEnum;
import com.eastfair.venueservice.enumeration.ServiceOrderOrderSourceEnum;
import com.eastfair.venueservice.enumeration.ServiceOrderPayStatusEnum;
import com.eastfair.venueservice.vo.ExhibitionSettlementDetailVO;
import com.eastfair.venueservice.vo.ExhibitionSettlementVO;
import com.eastfair.venueservice.vo.ServiceBookingVO;
import com.eastfair.venueservice.vo.ServiceOrderVO;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 业务接口
 * 服务订单
 * </p>
 *
 * @author dq
 * @date 2022 -09-28
 */
public interface ServiceOrderService extends SuperService<ServiceOrder> {

    int CANCEL_TIME_OUT = 1;

    /**
     * 分页查询服务订单列表.
     *
     * @param pageParams the page params
     * @return the page
     */
    Page<ServiceOrderVO> listServiceOrderVOs(PageParams<ServiceOrderDTO> pageParams);

    /**
     * 查询服务订单.
     *
     * @param serviceOrderDTO the service order dto
     * @return the list
     */
    List<ServiceOrder> listServiceOrders(ServiceOrderDTO serviceOrderDTO);

    /**
     * 订单编号获取服务订单VO.
     *
     * @param orderNumber the order number
     * @return the vo by order number
     */
    ServiceOrderVO getVOByOrderNumber(String orderNumber);

    /**
     * 订单编号获取服务订单.
     *
     * @param orderNumber the order number
     * @return the by order number
     */
    ServiceOrder getByOrderNumber(String orderNumber);

    /**
     * 报修单号查询报修订单.
     *
     * @param repairReportNumber   the repair report number
     * @param orderOrderSourceEnum the order order source enum
     * @return the by repair report number
     */
    ServiceOrder getByRepairReportNumber(String repairReportNumber, ServiceOrderOrderSourceEnum orderOrderSourceEnum);

    /**
     * 查询超时为支付订单.
     *
     * @param serviceOrderDTO the service order dto
     * @return the list
     */
    List<ServiceOrder> listTimeoutNotPayServiceOrder(ServiceOrderDTO serviceOrderDTO);

    /**
     * 保存服务订单.
     *
     * @param serviceOrderDTO the service order dto
     * @return the boolean
     */
    ServiceOrder saveServiceOrder(ServiceOrderDTO serviceOrderDTO);

    /**
     * 更新订单.
     *
     * @param serviceOrderDTO the service order dto
     * @return the boolean
     */
    ServiceOrder updateServiceOrder(ServiceOrderDTO serviceOrderDTO);

    /**
     * 更新订单为已提交.
     *
     * @param serviceOrder the service order
     * @return the service order
     */
    ServiceOrder updateOrderStatusSubmit(ServiceOrder serviceOrder);

    /**
     * 提交订单.
     *
     * @param orderNumber order number
     * @return the boolean
     */
    boolean submitServiceOrder(String orderNumber);

    /**
     * 更新订单状态为已完成.
     *
     * @param serviceOrder the service order
     * @return the boolean
     */
    boolean updateOrderStatusComplete(ServiceOrder serviceOrder);

    /**
     * 更新支付状态.
     *
     * @param serviceOrderDTO the service order dto
     * @return the boolean
     */
    ServiceOrder updatePayStatus(ServiceOrderDTO serviceOrderDTO);

    /**
     * 更新退款状态
     *
     * @param serviceOrderDTO the service order dto
     * @return the service order
     */
    ServiceOrder updateRefundStatus(ServiceOrderDTO serviceOrderDTO);

    /**
     * 更新开票状态
     *
     * @param serviceOrderDTO the service order dto
     * @return the service order
     */
    ServiceOrder updateInvoiceStatus(ServiceOrderDTO serviceOrderDTO);

    /**
     * 发起退款申请
     *
     * @param serviceOrderDTO the service order dto
     * @return the boolean
     */
    boolean applyServiceOrderRefund(ServiceOrderDTO serviceOrderDTO);

    /**
     * 生成交易记录.
     *
     * @param serviceOrder    the service order
     * @param serviceOrderDTO the service order dto
     * @return the boolean
     */
    boolean saveTransaction(ServiceOrder serviceOrder, ServiceOrderDTO serviceOrderDTO);

    /**
     * 生成支付系统交易订单.
     *
     * @param serviceOrderDTO the service order
     * @return the boolean
     */
    boolean saveTransactionOrder(ServiceOrderDTO serviceOrderDTO);

    /**
     * 发起支付中心申请退款
     *
     * @param serviceOrderDTO the service order
     * @return the boolean
     */
    boolean acceptApplyRefund(ServiceOrderDTO serviceOrderDTO);

    /**
     * 企业是否存在订单.
     *
     * @param companyId the company id
     * @return the boolean
     */
    boolean existServiceOrderByCompanyId(Long companyId);


    /**
     * 订单数据分析
     *
     * @param serviceOrderDTO the service order dto
     * @return map
     */
    Map<String,Object> orderDataAnalysis(ServiceOrderDTO serviceOrderDTO);

    /**
     * Order amount trend map.
     *
     * @param serviceOrderDTO the service order dto
     * @return the map
     */
    Map<String, Object> orderAmountTrend(ServiceOrderDTO serviceOrderDTO);

    /**
     * Order num trend map.
     *
     * @param serviceOrderDTO the service order dto
     * @return the map
     */
    Map<String, Object> orderNumTrend(ServiceOrderDTO serviceOrderDTO);

    /**
     * 通过展会查询搭建商
     *
     * @param exhibitionManageId exhibitionManageId
     * @return List<ExhibitionSettlementVO> list
     */
    List<ExhibitionSettlementVO> queryBuilder(Long exhibitionManageId);

    /**
     * 查询订单的优惠金额
     * @param orderNumbers orderNumbers
     * @return ServiceOrderVO
     */
    ServiceOrderVO queryTotalDiscountAmount(Set<String> orderNumbers);

    /**
     * 封装服务预定列表
     *
     * @param serviceOrderList the service order list
     * @return the list
     */
    List<ServiceOrderVO> packageServiceBookingList(List<ServiceOrder> serviceOrderList);

    /**
     * 订单导出.
     *
     * @param serviceOrderDTO the service order dto
     * @param response        the response
     * @throws IOException the io exception
     */
    void exportServiceOrder(ServiceOrderDTO serviceOrderDTO, HttpServletResponse response) throws IOException;

    /**
     * 计算订单金额.
     *
     * @param serviceOrderDTO          the service order dto
     * @param commonServiceBookingList the common service booking list
     */
    static void calculateOrderAmount(ServiceOrderDTO serviceOrderDTO, List<CommonServiceBookingDTO> commonServiceBookingList) {
        if (commonServiceBookingList == null || commonServiceBookingList.isEmpty()) {
            return;
        }
        List<ServiceBookingDTO> serviceBookingList = commonServiceBookingList
                .stream()
                .map(commonServiceBookingDTO -> BeanUtil.toBean(commonServiceBookingDTO, ServiceBookingDTO.class))
                .collect(Collectors.toList());
        serviceOrderDTO.setServiceBookingList(serviceBookingList);
        calculateOrderAmount(serviceOrderDTO);
    }

    /**
     * 计算订单金额.
     *
     * @param serviceOrderDTO the service order dto
     */
    static void calculateOrderAmount(ServiceOrderDTO serviceOrderDTO) {
        // 计算订单的总金额，和每项服务的金额
        // 附加费合计
        BigDecimal totalSurcharge = BigDecimal.ZERO;
        // 服务合计
        BigDecimal totalServicePrice = BigDecimal.ZERO;
        // 服务预定列表参数
        List<ServiceBookingDTO> serviceBookingList = serviceOrderDTO.getServiceBookingList();
        for (ServiceBookingDTO serviceBookingDTO : serviceBookingList) {
            // 预订数量
            Long bookingNum = serviceBookingDTO.getBookingNum();
            // 单价
            BigDecimal unitPrice = serviceBookingDTO.getUnitPrice();
            // 折扣 百分比
            BigDecimal discount = (Objects.nonNull(serviceBookingDTO.getDiscount()) ? serviceBookingDTO.getDiscount() : new BigDecimal("100"))
                    .divide(new BigDecimal("100"), 4, RoundingMode.DOWN)
                    .setScale(4, RoundingMode.DOWN);
            // 计算折后单价 向下取整
            BigDecimal unitPriceAfterDiscount = unitPrice
                    .multiply(discount)
                    .setScale(2, RoundingMode.DOWN);
            serviceBookingDTO.setUnitPriceAfterDiscount(unitPriceAfterDiscount);
            // 服务总价金额 向下取整
            BigDecimal subtotalServicePrice = unitPriceAfterDiscount
                    .multiply(BigDecimal.valueOf(bookingNum))
                    .setScale(2, RoundingMode.DOWN);
            serviceBookingDTO.setSubtotalServicePrice(subtotalServicePrice);
            // 附加费金额 向下取整
            BigDecimal surcharge = Objects.nonNull(serviceBookingDTO.getSurcharge()) ? serviceBookingDTO.getSurcharge() : BigDecimal.ZERO;
            BigDecimal subtotalSurchargePrice = BigDecimal
                    .valueOf(bookingNum)
                    .multiply(Objects.nonNull(surcharge) ? surcharge : BigDecimal.ZERO)
                    .setScale(2, RoundingMode.DOWN);
            serviceBookingDTO.setSubtotalSurchargePrice(subtotalSurchargePrice);
            // 小计 向下取整
            BigDecimal subtotalPrice = subtotalServicePrice.add(subtotalSurchargePrice);
            serviceBookingDTO.setSubtotalPrice(subtotalPrice);
            totalSurcharge = totalSurcharge.add(subtotalSurchargePrice);
            totalServicePrice = totalServicePrice.add(subtotalServicePrice);
        }
        // 附加费总金额
        serviceOrderDTO.setSurcharge(totalSurcharge);
        // 总金额
        serviceOrderDTO.setTotalAmount(totalServicePrice);
        // 应付总金额
        serviceOrderDTO.setShouldPayAmount(totalServicePrice);
    }

    /**
     * 计算订单的结算单
     *
     * @param serviceOrderList the service order list
     * @return the exhibition settlement vo
     */
    static ExhibitionSettlementVO calculateExhibitionSettlement(List<ServiceOrderVO> serviceOrderList) {
        // 转换为结算单
        ExhibitionSettlementVO exhibitionSettlementVO = new ExhibitionSettlementVO();
        List<ExhibitionSettlementDetailVO> exhibitionSettlementDetailList = new ArrayList<>();
        BigDecimal totalDiscountAmount = BigDecimal.ZERO;
        BigDecimal totalInvoicedAmount = BigDecimal.ZERO;
        BigDecimal totalPayAmount = BigDecimal.ZERO;
        for (ServiceOrderVO serviceOrderVO : serviceOrderList) {
            List<ServiceBookingVO> serviceBookingList = serviceOrderVO.getServiceBookingList();
            if (serviceBookingList == null || serviceBookingList.isEmpty()) {
                continue;
            }
            // 计算订单的优惠金额总计
            BigDecimal discountAmount = serviceOrderVO.getDiscountAmount() == null ? BigDecimal.ZERO : serviceOrderVO.getDiscountAmount();
            totalDiscountAmount = totalDiscountAmount.add(discountAmount);
            // 已开票金额
            if (ServiceOrderInvoiceStatusEnum.INVOICED.eq(serviceOrderVO.getInvoiceStatus())) {
                BigDecimal invoiceAmount = serviceOrderVO.getInvoiceAmount() == null ? BigDecimal.ZERO : serviceOrderVO.getInvoiceAmount();
                totalInvoicedAmount = totalInvoicedAmount.add(invoiceAmount);
            }
            // 已付款金额
            if (ServiceOrderPayStatusEnum.HAS_PAY.eq(serviceOrderVO.getPayStatus()) || ServiceOrderPayStatusEnum.PARTIAL_PAY.eq(serviceOrderVO.getPayStatus())) {
                BigDecimal actualPayAmount = serviceOrderVO.getActualPayAmount() == null ? BigDecimal.ZERO : serviceOrderVO.getActualPayAmount();
                totalPayAmount = totalPayAmount.add(actualPayAmount);
            }
            // 判断订单类型
            ExhibitionSettlementDetailSettlementTypeEnum settlementType;
            if (serviceOrderVO.getIsManageExpense() == BusinessConstant.YES) {
                settlementType = ExhibitionSettlementDetailSettlementTypeEnum.MANAGE;
            } else {
                if (ServiceOrderOrderSourceEnum.VENUE_HOME_REPAIR_ORDER.eq(serviceOrderVO.getOrderSource()) ||
                        ServiceOrderOrderSourceEnum.HOME_BUILDER_REPAIR_ORDER.eq(serviceOrderVO.getOrderSource())) {
                    settlementType = ExhibitionSettlementDetailSettlementTypeEnum.REPAIR_REPORT;
                } else {
                    settlementType = ExhibitionSettlementDetailSettlementTypeEnum.SERVICE;
                }
            }
            // 封装预定服务为结算详情
            for (ServiceBookingVO serviceBookingVO : serviceBookingList) {
                ExhibitionSettlementDetailVO exhibitionSettlementDetailVO = toExhibitionSettlementDetail(serviceBookingVO, settlementType);
                exhibitionSettlementDetailList.add(exhibitionSettlementDetailVO);
            }
        }
        exhibitionSettlementVO.setReceived(totalPayAmount);
        exhibitionSettlementVO.setHasInvoicedAmount(totalInvoicedAmount);
        exhibitionSettlementVO.setDiscountAmount(totalDiscountAmount);
        exhibitionSettlementVO.setExhibitionSettlementDetail(exhibitionSettlementDetailList);
        return exhibitionSettlementVO;
    }

    /**
     * 转换为结算详情VO
     *
     * @param serviceBookingVO the service booking vo
     * @return the exhibition settlement detail vo
     */
    static ExhibitionSettlementDetailVO toExhibitionSettlementDetail(ServiceBookingVO serviceBookingVO, ExhibitionSettlementDetailSettlementTypeEnum settlementType) {
        ExhibitionSettlementDetailVO exhibitionSettlementDetailVO = new ExhibitionSettlementDetailVO();
        exhibitionSettlementDetailVO.setSettlementType(settlementType);
        exhibitionSettlementDetailVO.setBusinessNo(serviceBookingVO.getOrderNumber());
        exhibitionSettlementDetailVO.setName(serviceBookingVO.getServiceName());
        exhibitionSettlementDetailVO.setServiceCategoryName(serviceBookingVO.getServiceCategoryName());
        exhibitionSettlementDetailVO.setServiceSpecsName(serviceBookingVO.getServiceSpecsName());
        exhibitionSettlementDetailVO.setNum(serviceBookingVO.getBookingNum());
        exhibitionSettlementDetailVO.setUnitPrice(serviceBookingVO.getUnitPrice());
        exhibitionSettlementDetailVO.setSubtotalPrice(serviceBookingVO.getSubtotalPrice());
        return exhibitionSettlementDetailVO;
    }

    /**
     * 转换为支付系统订单.
     *
     * @param serviceOrderDTO the service order dto
     * @return the transaction order dto
     */
    static TransactionOrderDTO toTransactionOrder(ServiceOrderDTO serviceOrderDTO) {
        TransactionOrderDTO transactionOrderDTO = new TransactionOrderDTO();
        transactionOrderDTO.setExhibitionId(serviceOrderDTO.getExhibitionManageId());
        transactionOrderDTO.setExhibitionName(serviceOrderDTO.getExhibitionManageName());
        transactionOrderDTO.setOrderType(TransactionOrderOrderTypeEnum.SERVICE);
        transactionOrderDTO.setOrderNum(serviceOrderDTO.getOrderNumber());
        transactionOrderDTO.setOrderAmount(serviceOrderDTO.getShouldPayAmount());
        transactionOrderDTO.setOrderTime(serviceOrderDTO.getOrderTime());
        transactionOrderDTO.setOrderStatus(TransactionOrderOrderStatusEnum.UNPAY);
        transactionOrderDTO.setPayer(serviceOrderDTO.getCompanyName());
        transactionOrderDTO.setPayee(serviceOrderDTO.getPayee());
        transactionOrderDTO.setPaymentPeriods(1);
        transactionOrderDTO.setSystemCode(serviceOrderDTO.getSystemCode());
        return transactionOrderDTO;
    }

}
